/*
	Authors: Samuel Thacker and Faiz Rahman
	Description: This is a header file which defines basic math funtions between two operators.
	Updates:
		2012/01/17 - Added '@', '=', and letter operators.  Also, added the ability to store both registers and history.
		2012/01/14 - Added the support of 3 strings.
		2012/01/12 - Creation of the math function, the test function, and had the program capable of doing math with 2 entered strings.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

/*Math Functions*/

double bi_plus(double ans,double num) /*Adds 'num' and 'ans' together.*/
	{
	return (ans + num);
	}

double bi_minus(double ans,double num) /*Subtracts 'num' from 'ans', and stores it in 'ans'.*/
	{
	return (ans - num);
	}

double multiply(double ans,double num) /*Multiplies 'num' and 'ans', and stores it in 'ans'.*/
	{
	return (ans * num);
	}

double divide(double ans,double num) /*Divides 'ans' by 'num', and stores it in 'ans'.*/
	{
	return (ans / num);
	}

/*Test Function*/

char test(char * str) /*Returns an interger value that describes what the function does.*/
	{
	/*Initialization*/
	char a;
	unsigned int x,t;

	if ((str[0] >= 'a' && str[0] <= 'z') || (str[0] >= 'A' && str[0] <= 'Z')) /*Test for letters.*/
		{
		if (strlen(str) > 1) /*Tests to see if there is anything besides a lower case letter*/
			{
			return 0; /*Syntax Error if so.*/
			}
		return 10; /*Returns 10 if a letter.*/
		}

	if (str[0] == '/' || str[0] == '*' ||str[0] == '=') /*Tests for multiplication sign, division sign, or equal sign.*/
		{
		if (strlen(str) > 1) /*Tests to see if there is anything besides the '/' or '*'*/
			{
			return 0; /*Syntax Error if so.*/
			}
		if (str[0] == '*') /*Return 6 if '*', 7 if '/', 9 if '='*/
			return 6;
		if (str[0] == '/')
			return 7;
		return 9;
		}

	if (str[0] == '+' || str[0] == '-' || str[0] == '@') /*Tests for addition sign or subtraction sign.*/
		{
		if (strlen(str) == 1) /*Tests to see if there is anything besides the '-' or '+'*/
			{
			if (str[0] == '+') /*Return 4 if '+', 5 if '-', or 8 if '@'*/
				return 4;
			if (str[0] == '-')
				return 5;
			return 8;
			}

		if ((str[1] >= 'a' && str[1] <= 'z') || (str[1] >= 'A' && str[1] <= 'Z')) /*Test for letters.*/
			{
			if (strlen(str) > 2) /*Tests to see if there is anything besides a lower case letter and the sign*/
				{
				return 0; /*Syntax Error if so.*/
				}
			if (str[0] <= '+') /*Return 11 if unary '+', 12 if unary '-', or 8 if '@'*/
				return 11;
			if (str[0] == '-')
				return 12;
			return 8;
			}

		x = t = 1;
		a = str[1];
		while(a != '\0') /*Tests until the end of the string.*/
			{
			if (t == 1 && str[x] == '.')
				{
				t = 0; /*Sets the limit for '.' to 1*/
				}
			else if (!(str[x] >= '0' && str[x] <= '9')) /*If not a number, then indicate that it is an Syntax Error.*/
				{
				return 0; /*If not a number, '.', or there is more then 1 '.'*/
				}
			x++;
			a = str[x];
			}
		if (str[0] == '+')
			return 2; /*Returns 2 if unary plus operation.*/
		return 3; /*Returns 3 if unary minus operation.*/
		/*2 for unary '+', 3 for unary '-'*/
		}

	x = t = 0;
	a = str[0];
	while(a != '\0') /*Tests until the end of the string.*/
		{
		if (t == 0 && str[x] == '.')
			{
			t = 1; /*Sets the limit for '.' to 1*/
			}
		else if (!(str[x] >= '0' && str[x] <= '9')) /*If not a number, then indicate that it is an Syntax Error.*/
			{
			return 0; /*If not a number, '.', or there is more then 1 '.'*/
			}
		x++;
		a = str[x];
		}
	return 1; /*Returns that the user entered in a number.*/
		
	} /*END char test(char * str)*/


/* Main Function */

int main()
	{
	/*Initialization*/
	double ACC, tACC, abc[26] = {0};
	char *cp, Stemp1[100], Stemp2[100], Stemp3[100], over[1], temp;
	int ret;
	double (*math)(double,double);
	FILE * fp;

	if ((fp = fopen("register.bin", "r"))) /* Checks to see if the file exists */
		{
		fread(abc, 8, 26,fp);
        fclose(fp);
		}

	if ((fp = fopen("history.bin", "r"))) /* Checks to see if the file exists */
		{
		read_history ("history.bin");
        fclose(fp);
		}

	ACC = 0;

	while(1) /*Main Loop*/
		{
		sprintf(Stemp1,"%g: ",ACC);
		cp = readline(Stemp1);
		add_history(cp);
		ret = sscanf(cp,"%s %s %s %s",Stemp1,Stemp2,Stemp3,over);
		free(cp);
		if (ret == -1) /* If user hits enter */
			{
			/* File IO */

			/* Register file */
			fp = fopen("register.bin", "w");
			fwrite (abc , 8 , 26 , fp);
			fclose(fp);
			/* History file */
			fp = fopen("history.bin", "w");
			write_history("history.bin");
			fclose(fp);
			return 0;
			}

		else if (ret == 1) /*If 1 string is found.*/
			{
			switch (test(Stemp1))
				{
				case 1: /*Basic Assignment*/
					ACC = atof(Stemp1);
					break;
				case 2: /*'+' Assignment*/
					ACC = atof(Stemp1 + sizeof(char));
					break;
				case 3: /*'-' Assignment*/
					ACC = -atof(Stemp1 + sizeof(char));
					break;
				case 4: /*Unary '+'*/
					break;
				case 5: /*Unary '-'*/
					if (ACC != 0)
						ACC = -ACC;
					break;
				case 8: /*Variable Assignment*/
					if (Stemp1[1] <= 'Z') /*Tests to see if uppercase or lowercase.*/
						{
						abc[Stemp1[1] - 'A'] = ACC; /*If upper.*/
						break;
						}
					abc[Stemp1[1] - 'a'] = ACC; /*If lower/*/
					break;
				case 10: /*Letter Assignment*/
					if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
						{
						ACC = abc[Stemp1[0] - 'A']; /*If upper.*/
						break;
						}
					ACC = abc[Stemp1[0] - 'a']; /*If lower/*/
					break;
				case 11: /*'+' Letter Assignment*/
					if (Stemp1[1] <= 'Z') /*Tests to see if uppercase or lowercase.*/
						{
						ACC = abc[Stemp1[1] - 'A']; /*If upper.*/
						break;
						}
					ACC = abc[Stemp1[1] - 'a']; /*If lower/*/
					break;
				case 12: /*'-' Letter Assignment*/
					if (Stemp1[1] <= 'Z') /*Tests to see if uppercase or lowercase.*/
						{
						ACC = -abc[Stemp1[1] - 'A']; /*If upper.*/
						break;
						}
					ACC = -abc[Stemp1[1] - 'a']; /*If lower/*/
					break;
				default:
					printf("Syntax Error\n");
					break;
				}	
			}

		else if (ret == 2) /*If 2 strings are found.*/
			{
			temp = 0;

			switch (test(Stemp1))
				{
				case 4:
					math = bi_plus; /*addition*/
					temp = 1;
					break;
				case 5:
					math = bi_minus; /*subtraction*/
					temp = 1;
					break;
				case 6:
					math = multiply; /*multiplication*/
					temp = 1;
					break;
				case 7:
					math = divide; /*division*/
					temp = 1;
					break;
				case 8:
					if (test(Stemp2) == 10)
						temp = 2;
					else
						printf("Syntax Error\n");
					break;
				default:
					printf("Syntax Error\n");
					break;
				}

			if (temp == 1)
				{
				switch (test(Stemp2))
					{
					case 1: /*Basic Number*/
						if (math == divide && atof(Stemp2) == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(ACC,atof(Stemp2));
						break;
					case 2: /*Unary '+' Number*/
						if (math == divide && atof(Stemp2) == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(ACC,atof(Stemp2 + sizeof(char)));
						break;
					case 3: /*Unary '-' Number*/
						if (math == divide && atof(Stemp2) == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(ACC,-atof(Stemp2 + sizeof(char)));
						break;
					case 10: /*Basic Letter*/
						if (Stemp2[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
							{
							if (math == divide && abc[Stemp2[0] - 'A'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(ACC,abc[Stemp2[0] - 'A']);
							}
						else
							{
							if (math == divide && abc[Stemp2[0] - 'a'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(ACC,abc[Stemp2[0] - 'a']);
							}
						break;
					case 11: /*Unary '+' Letter*/
						if (Stemp2[1] <= 'Z') /*Tests to see if uppercase or lowercase.*/
							{
							if (math == divide && abc[Stemp2[1] - 'A'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(ACC,abc[Stemp2[1] - 'A']);
							}
						else
							{
							if (math == divide && abc[Stemp2[1] - 'a'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(ACC,abc[Stemp2[1] - 'a']);
							}
						break;
					case 12: /*Unary '-' Letter*/
						if (Stemp2[1] <= 'Z')
							{
							if (math == divide && abc[Stemp2[1] - 'A'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(ACC,-abc[Stemp2[1] - 'A']);
							}
						else
							{
							if (math == divide && abc[Stemp2[1] - 'a'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(ACC,-abc[Stemp2[1] - 'a']);
							}
						break;
					default:
						printf("Syntax Error\n");
						break;
					}
				}
			else if (temp == 2)
				{
				if (Stemp2[0] <= 'Z')
					abc[Stemp2[0] - 'A'] = ACC;
				else
					abc[Stemp2[0] - 'a'] = ACC;
				}
			}

		else if (ret == 3) /*If 3 strings are found.*/
			{
			temp = 0;

			switch (test(Stemp2))
				{
				case 4: /*addition*/
					math = bi_plus;
					temp = 1; 
					break;
				case 5: /*subtraction*/
					math = bi_minus;
					temp = 1;
					break;
				case 6: /*multiplication*/
					math = multiply;
					temp = 1;
					break;
				case 7: /*division*/
					math = divide;
					temp = 1;
					break;
				case 9: /*'=' assignment*/
					if (test(Stemp1) == 10)
						temp = 3;
					else
						printf("Syntax Error\n");
					break;
				default:
					printf("Syntax Error\n");
					break;
				}

			if (temp == 1)
				{
				switch (test(Stemp1))
					{
					case 1: /*Basic Assignment*/
						tACC = atof(Stemp1);
						temp = 2;
						break;
					case 2: /*'+' Assignment*/
						tACC = atof(Stemp1 + sizeof(char));
						temp = 2;
						break;
					case 3: /*'-' Assignment*/
						tACC = -atof(Stemp1 + sizeof(char));
						temp = 2;
						break;
					case 10: /*Letter Assignement*/
						if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
							{
							tACC = abc[Stemp1[0] - 'A']; /*If upper.*/
							break;
							}
						tACC = abc[Stemp1[0] - 'a']; /*If lower/*/
						temp = 2;
						break;
					case 11: /*'+' Letter Assignement*/
						if (Stemp1[1] <= 'Z') /*Tests to see if uppercase or lowercase.*/
							{
							tACC = abc[Stemp1[1] - 'A']; /*If upper.*/
							break;
							}
						tACC = abc[Stemp1[1] - 'a']; /*If lower/*/
						temp = 2;
						break;
					case 12: /*'-' Letter Assignement*/
						if (Stemp1[1] <= 'Z') /*Tests to see if uppercase or lowercase.*/
							{
							tACC = -abc[Stemp1[1] - 'A']; /*If upper.*/
							break;
							}
						tACC = -abc[Stemp1[1] - 'a']; /*If lower/*/
						temp = 2;
						break;
					default:
						printf("Syntax Error\n");
						break;
					}
				}

			if (temp >= 2)
				{
				switch (test(Stemp3))
					{
					case 1: /*Basic Number*/
						if (temp == 3) /*if variable assignment*/
							{
							if (Stemp1[0] <= 'Z')
								abc[Stemp1[0] - 'A'] = atof(Stemp3);
							else
								abc[Stemp1[0] - 'a'] = atof(Stemp3);
							break;
							}
						if (math == divide && atof(Stemp3) == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(tACC,atof(Stemp3));
						break;
					case 2: /*Unary '+' Number*/
						if (temp == 3) /*if variable assignment*/
							{
							if (Stemp1[0] <= 'Z')
								abc[Stemp1[0] - 'A'] = atof(Stemp3 + sizeof(char));
							else
								abc[Stemp1[0] - 'a'] = atof(Stemp3 + sizeof(char));
							break;
							}
						if (math == divide && atof(Stemp3 + sizeof(char)) == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(tACC,atof(Stemp3 + sizeof(char)));
						break;
					case 3: /*Unary '-' Number*/
						if (temp == 3) /*if variable assignment*/
							{
							if (Stemp1[0] <= 'Z')
								abc[Stemp1[0] - 'A'] = -atof(Stemp3 + sizeof(char));
							else
								abc[Stemp1[0] - 'a'] = -atof(Stemp3 + sizeof(char));
							break;
							}
						if (math == divide && atof(Stemp3 + sizeof(char)) == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(tACC,-atof(Stemp3 + sizeof(char)));
						break;
					case 10: /*Basic Letter*/
						if (Stemp3[0] <= 'Z')/*Tests to see if uppercase or lowercase.*/
							{
							/*if upper case*/
							if (temp == 3) /*if variable assignment*/
								{
								if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
									abc[Stemp1[0] - 'A'] = abc[Stemp3[0] - 'A'];
								else
									abc[Stemp1[0] - 'a'] = abc[Stemp3[0] - 'A'];
								break;
								}
							if (math == divide && abc[Stemp3[0] - 'A'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(tACC,abc[Stemp3[0] - 'A']);
							break;
							}
						/*if lower case*/
						if (temp == 3) /*if variable assignment*/
							{
							if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
								abc[Stemp1[0] - 'A'] = abc[Stemp3[0] - 'a'];
							else
								abc[Stemp1[0] - 'a'] = abc[Stemp3[0] - 'a'];
							break;
							}
						if (math == divide && abc[Stemp3[0] - 'a'] == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(tACC,abc[Stemp3[0] - 'a']);
						break;
					case 11: /*'+' Letter*/
						if (Stemp3[1] <= 'Z')/*Tests to see if uppercase or lowercase.*/
							{
							/*if lower case*/
							if (temp == 3) /*if variable assignment*/
								{
								if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
									abc[Stemp1[0] - 'A'] = abc[Stemp3[1] - 'A'];
								else
									abc[Stemp1[0] - 'a'] = abc[Stemp3[1] - 'A'];
								break;
								}
							if (math == divide && abc[Stemp3[1] - 'A'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(tACC,abc[Stemp3[1] - 'A']);
							break;
							}
						/*if lower case*/
						if (temp == 3) /*if variable assignment*/
							{
							if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
								abc[Stemp1[0] - 'A'] = abc[Stemp3[1] - 'a'];
							else
								abc[Stemp1[0] - 'a'] = abc[Stemp3[1] - 'a'];
							break;
							}
						if (math == divide && abc[Stemp3[1] - 'a'] == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(tACC,abc[Stemp3[1] - 'a']);
						break;
					case 12: /*'-' Letter*/
						if (Stemp3[1] <= 'Z')/*Tests to see if uppercase or lowercase.*/
							{
							/*if lower case*/
							if (temp == 3) /*if variable assignment*/
								{
								if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
									abc[Stemp1[0] - 'A'] = -abc[Stemp3[1] - 'A'];
								else
									abc[Stemp1[0] - 'a'] = -abc[Stemp3[1] - 'A'];
								break;
								}
							if (math == divide && abc[Stemp3[1] - 'A'] == 0)
								{
								printf("Can not divide by 0.\n");
								break;
								}
							ACC = math(tACC,-abc[Stemp3[1] - 'A']);
							break;
							}
						/*if lower case*/
						if (temp == 3) /*if variable assignment*/
							{
							if (Stemp1[0] <= 'Z') /*Tests to see if uppercase or lowercase.*/
								abc[Stemp1[0] - 'A'] = -abc[Stemp3[1] - 'a'];
							else
								abc[Stemp1[0] - 'a'] = -abc[Stemp3[1] - 'a'];
							break;
							}
						if (math == divide && abc[Stemp3[1] - 'a'] == 0)
							{
							printf("Can not divide by 0.\n");
							break;
							}
						ACC = math(tACC,-abc[Stemp3[1] - 'a']);
						break;
					default:
						printf("Syntax Error\n");
						break;
					}
				}
			}
		else /*Too many strings.*/
			{
			printf("Syntax Error\n");
			}
		}
	} /*END int main()*/
